package com.song.util;

import ch.hsr.geohash.GeoHash;
import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.algorithm.distance.DistanceToPoint;
import org.locationtech.jts.algorithm.distance.PointPairDistance;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
@Slf4j
public class GisUtil {
    public static GeometryFactory geometryFactory = new GeometryFactory();

    public static WKTReader wktReader = new WKTReader();

    /**
     * 判断目标是否包含在源数据内
     * @param target 目标字符串（例：POINT(1 1) LINESTRING(1 1, 2 2)）
     * @param source 源数据字符串（例：POLYGON((0 1, 2 2, 4 5, 0 1)) MULTIPOLYGON(((0 1, 2 2, 4 5, 0 1)), ((0 2, 1 2, 4 5, 0 2)))）
     * @return
     */
    public static boolean contains(String target, String source){
        try {
            Geometry targetGeo = wktReader.read(target);
            Geometry sourceGeo = wktReader.read(source);
            return sourceGeo.contains(targetGeo);
        } catch (Exception e) {
            log.error("GIS method 'contains' failed. exception message: {}", e.getMessage());
        }
        return false;
    }

    /**
     * 判断坐标点是否在区域内（不包含边缘）
     * @param coordinate 目标点坐标
     * @param coordinates 区域坐标数组
     * @return
     */
    public static boolean within(Coordinate coordinate, Coordinate[] coordinates){
        boolean result = false;
        Point point = geometryFactory.createPoint(coordinate);
        Polygon polygon = geometryFactory.createPolygon(coordinates);
        //判断区域是否闭合
        if (!CoordinateArrays.isRing(coordinates)){
            return result;
        }
        result = point.within(polygon);
        return result;
    }

    /**
     * 判断坐标点是否与区域相交
     * @param coordinate
     * @param coordinates
     * @return
     */
    public static boolean intersects(Coordinate coordinate, Coordinate[] coordinates){
        boolean result = false;
        Point point = geometryFactory.createPoint(coordinate);
        Polygon polygon = geometryFactory.createPolygon(coordinates);
        if (!CoordinateArrays.isRing(coordinates)){
            return result;
        }
        result = point.intersects(polygon);
        return result;
    }

    /**
     * 判断坐标点是否在区域内（包含边缘）
     * @param coordinate
     * @param coordinates
     * @return
     */
    public static boolean withinOrIntersects(Coordinate coordinate, Coordinate[] coordinates){
        boolean result = false;
        Point point = geometryFactory.createPoint(coordinate);
        Polygon polygon = geometryFactory.createPolygon(coordinates);
        if (!CoordinateArrays.isRing(coordinates)){
            return result;
        }
        result = point.within(polygon) || point.intersects(polygon);
        return result;
    }

    /**
     * 计算两个坐标点之间的距离
     * @param longitude1 坐标1经度
     * @param latitude1 坐标1纬度
     * @param longitude2 坐标2经度
     * @param latitude2 坐标2纬度
     * @return  距离
     */
    public static double getDistance(double longitude1, double latitude1, double longitude2, double latitude2){
        Point point = geometryFactory.createPoint(new Coordinate(longitude1, latitude1));
        PointPairDistance pointPairDistance = new PointPairDistance();
        DistanceToPoint.computeDistance(point, new Coordinate(longitude2, latitude2), pointPairDistance);
        return pointPairDistance.getDistance();
    }

    /**
     * 判断坐标之间的距离（高德地图算法）
     * @param longitude1 坐标1经度
     * @param latitude1 坐标1纬度
     * @param longitude2 坐标2经度
     * @param latitude2 坐标2纬度
     * @return 距离（米）
     */
    public static double getDistanceGd(double longitude1, double latitude1, double longitude2, double latitude2){
        if(longitude1==0 || latitude1==0 || longitude2==0 || latitude2==0){
            return -1.0;
        }
        longitude1 *= 0.01745329251994329;
        latitude1 *= 0.01745329251994329;
        longitude2 *= 0.01745329251994329;
        latitude2 *= 0.01745329251994329;
        double var10 = Math.sin(longitude1);
        double var12 = Math.sin(latitude1);
        double var14 = Math.cos(longitude1);
        double var16 = Math.cos(latitude1);
        double var18 = Math.sin(longitude2);
        double var20 = Math.sin(latitude2);
        double var22 = Math.cos(longitude2);
        double var24 = Math.cos(latitude2);
        double[] var28 = new double[3];
        double[] var29 = new double[3];
        var28[0] = var16 * var14;
        var28[1] = var16 * var10;
        var28[2] = var12;
        var29[0] = var24 * var22;
        var29[1] = var24 * var18;
        var29[2] = var20;
        double distance = Math.asin(Math.sqrt((var28[0] - var29[0]) * (var28[0] - var29[0]) + (var28[1] - var29[1]) * (var28[1] - var29[1]) + (var28[2] - var29[2]) * (var28[2] - var29[2])) / 2.0) * 1.27420015798544E7;
        return distance ;
    }

    /**
     * @param longitude 经度
     * @param latitude 纬度
     * @param centerLon 圆心经度
     * @param centerLat 圆心纬度
     * @param radius 半径
     * @return
     */
    public static boolean isInCircle(double longitude, double latitude, double centerLon, double centerLat, double radius){
        Coordinate coordinate = new Coordinate(longitude, latitude);
        Point point = geometryFactory.createPoint(coordinate);
        Polygon circle = createCircle(centerLon, centerLat, radius);
        return point.within(circle);
    }

    /**
     * @param centerLon 圆心经度
     * @param centerLat 圆心纬度
     * @param RADIUS 半径
     * @return 圆
     */
    public static Polygon createCircle(double centerLon, double centerLat, final double RADIUS) {
        // 确定边数
        final int SIDES = 32;
        Coordinate coords[] = new Coordinate[SIDES + 1];
        for (int i = 0; i < SIDES; i++) {
            double angle = ((double) i / (double) SIDES) * Math.PI * 2.0;
            double dx = Math.cos(angle) * RADIUS;
            double dy = Math.sin(angle) * RADIUS;
            coords[i] = new Coordinate((double) centerLon + dx, (double) centerLat + dy);
        }
        coords[SIDES] = coords[0];
        // 画一个环线
        LinearRing ring = geometryFactory.createLinearRing(coords);
        // 生成一个面
        Polygon polygon = geometryFactory.createPolygon(ring, null);
        return polygon;
    }

    public static void main(String[] args) {
        String s = GeoHash.geoHashStringWithCharacterPrecision(39.9, 116.3, 12);
        System.out.println(s);
    }
}
